//
//  Calculator.cpp
//  Cryptarithmetic
//
//  Created by Derivery Guillaume on 4/23/13.
//  Copyright (c) 2013 Derivery Guillaume. All rights reserved.
//

#include "Calculator.h"


Calculator::Calculator(std::vector<std::string> const  param) : _numbers(param.size())
{
    this->_args = param;
}

bool Calculator::assignDefaultValueToEachCharacter()
{
  for (unsigned int idx = 0; idx < this->_args.size(); ++idx)
    {
        std::string &temp = this->_args[idx];
	std::vector<char*> number(temp.size());
        for (unsigned int i = 0; i < temp.size(); i++)
        {
            if (this->_characterAssigned.find(temp[i]) == this->_characterAssigned.end())
	      this->_characterAssigned[temp[i]] = '0';
	    number[i] = &this->_characterAssigned[temp[i]];
        }
	this->_numbers[idx] = number;
    }
    if (this->_characterAssigned.size() > 10)
      return false;
    return true;
}

void Calculator::printArgs()
{
  // factoriser !!
  unsigned int i = 0;
  unsigned int padding = this->getPadding();
  for (unsigned int idx = 0; idx < this->_args.size(); ++idx)
    {
      if (i == 0)
		std::cout << " ";
      for (unsigned int j = this->_args[i].size(); j < padding; ++j)
	      std::cout << " ";
      std::cout << "  ";
      std::cout << this->_args[idx].c_str() << std::endl;
      if (i == this->_args.size() - 2)
		std::cout << "=";
      else if (i < this->_args.size() - 1)
		std::cout << "+";
      i++;
    }
}

void Calculator::printResult()
{
  unsigned int i = 0;
  unsigned int padding = this->getPadding();
  std::cout << std::endl;
  if (!this->_found)
    return;
  i = 0;
  for (std::vector<std::vector<char*> >::iterator it = this->_numbers.begin(); it != this->_numbers.end(); ++it)
    {
      if (i == 0)
	std::cout << " ";
      std::cout << "  ";
      for (unsigned int j = this->_args[i].size(); j < padding; ++j)
	std::cout << " ";
      for (unsigned int j = 0; j < this->_args[i].size(); j++)
	std::cout << *this->_numbers[i][j];
      std::cout << std::endl;
      if (i == this->_numbers.size() - 2)
	std::cout << "=";
      else if (i < this->_numbers.size() - 1)
	std::cout << "+";
      i++;
    }
}

unsigned int Calculator::getPadding()
{
  unsigned int max = 0;
  for (std::vector<std::string >::iterator it = this->_args.begin(); it != this->_args.end(); ++it)
    if (it->size() > max)
      max = it->size();
  return max;    
}

bool Calculator::changeCharacters()
{
  if (this->_currentIt->second == '9')
    {
      std::map<char, char>::iterator tmpIt = this->_currentIt;
      while (true)
	{
	  if (tmpIt->second == '9')
	    tmpIt->second = '0';
	  else
	    {
	      ++tmpIt->second;
	      break;
	    }
	  if (tmpIt != this->_characterAssigned.begin())
	    --tmpIt;
	  else
	    return false;
	}
      this->_currentIt = this->_lastIt;
    }
  else
    ++this->_currentIt->second;
  return true;
}

int             Calculator::getNbr(const std::vector<char*> &str)
{
  int           res;
  unsigned int i = 0;

  res = 0;
  for (; i < str.size(); ++i)
    {
      res = res * 10;
      res = res + *str[i] - '0';
    }
  return (res);
}

int Calculator::addStrings()
{
  int res = 0;
  for (unsigned int i = 0; i < this->_numbers.size() - 1; ++i)
    res += this->getNbr(this->_numbers[i]);
  return res;
}

bool Calculator::checkResult()
{
  int total = this->getNbr(this->_numbers[this->_numbers.size() - 1]);
  return total == this->addStrings();
}

bool Calculator::checkDuplicateNumbers()
{
  for (std::map<char, char>::iterator itNumberToSearch = this->_characterAssigned.begin(); itNumberToSearch != this->_characterAssigned.end(); ++itNumberToSearch)
    for (std::map<char, char>::iterator it = this->_characterAssigned.begin(); it != this->_characterAssigned.end(); ++it)
      if (it != itNumberToSearch && 
	  itNumberToSearch->second == it->second)
	return true;
  return false;
}

bool Calculator::isItGood()
{
  return !this->checkDuplicateNumbers() && this->checkResult();
}

std::string * Calculator::getResultToDisplay()
{
    std::stringstream ss;
    unsigned int i = 0;
    unsigned int padding = this->getPadding();
    ss << std::endl;

    if (!this->_found)
        return NULL;
    i = 0;
    for (std::vector<std::vector<char*> >::iterator it = this->_numbers.begin(); it != this->_numbers.end(); ++it)
    {
        if (i == 0)
            ss << "  ";
        ss << "  ";
        for (unsigned int j = this->_args[i].size(); j < padding; ++j)
            ss << " ";
        for (unsigned int j = 0; j < this->_args[i].size(); j++)
        {
            ss << *this->_numbers[i][j];
            ss << " ";
        }
        ss << std::endl;
        if (i == this->_numbers.size() - 2)
        {
            for (unsigned int i = 0; i < (this->_numbers[this->_numbers.size() - 1].size() * 2) + 4; i++)
                ss << "-";
            ss<< std::endl;
            ss << "=";
        }
        else if (i < this->_numbers.size() - 1)
            ss << "+";
        i++;
    }
    std::string *temp = new std::string(ss.str());
    return temp;
}

bool Calculator::calculation()
{
  if (!this->assignDefaultValueToEachCharacter())
    {
      std::cout << "Too much different letters!" << std::endl;
      return false;
    }
  this->_lastIt = this->_characterAssigned.end();
  --this->_lastIt;
  this->_currentIt = this->_lastIt;
  this->printArgs();
  while (true)
    {
      if (this->isItGood())
	{
	  this->_found = true;
     // this->printResult();
         this->firstResult_  = this->getResultToDisplay();
          return true;
	}
      if (!this->changeCharacters())
	{
	  if (!this->_found)
	    std::cout << "Not found!" << std::endl;
	  return false;
	}      
    }
}
std::string *Calculator::getFirstResult()
{
    return this->firstResult_;
}
Calculator::~Calculator()
{
}
